package com.hyperf.spring.boot.autoconfigure;

import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.hyperf.core.web.AbstractInterceptor;
import com.hyperf.core.web.AbstractViewController;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * @author lizhaoyang
 */
@SuppressWarnings("all")
@Configuration
@ConditionalOnMissingClass
@ConditionalOnWebApplication
public class WebMvcAutoConfiguration implements WebMvcConfigurer, BeanPostProcessor {

    private LinkedHashSet<AbstractViewController> viewControllers = new LinkedHashSet<>();
    private LinkedHashSet<AbstractInterceptor> interceptors = new LinkedHashSet<>();

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        for (AbstractViewController viewController : viewControllers) {
            registry.addViewController(viewController.getUrlPath())
                    .setStatusCode(viewController.getStatusCode())
                    .setViewName(viewController.getViewName());
        }
    }

    /**
     * 配置请求拦截器
     * 1.加入的顺序就是拦截器执行的顺序
     * 2.按顺序执行所有拦截器的preHandle
     * 3.所有的preHandle 执行完再执行全部postHandle 最后是afterCompletion
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LinkedHashSet<AbstractInterceptor> sortInterceptors = sortInterceptors(interceptors);
        for (AbstractInterceptor interceptor : sortInterceptors) {
            registry.addInterceptor(interceptor)
                    .addPathPatterns(interceptor.getPathPatterns())
                    .excludePathPatterns(interceptor.getExcludePathPatterns());
        }
    }

    /**
     * 解决浏览器请求跨域的问题
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowCredentials(true);
    }

    /**
     * FastJson转换消息
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);

        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);

        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializeConfig(serializeConfig);
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.DisableCircularReferenceDetect);

        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        converter.setSupportedMediaTypes(fastMediaTypes);
        converter.setFastJsonConfig(fastJsonConfig);

        converters.add(0, converter);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        popluateInterceptors(bean);
        popluateViewControllers(bean);

        return bean;
    }

    private void popluateViewControllers(Object bean) {
        if (bean instanceof AbstractViewController) {
            viewControllers.add((AbstractViewController) bean);
        }
    }

    private void popluateInterceptors(Object bean) {
        if (bean instanceof AbstractInterceptor) {
            interceptors.add((AbstractInterceptor) bean);
        }
    }

    private LinkedHashSet<AbstractInterceptor> sortInterceptors(LinkedHashSet<AbstractInterceptor> interceptors) {
        LinkedHashSet<AbstractInterceptor> sortedInterceptors = new LinkedHashSet<>();
        List<AbstractInterceptor> unOrderinterceptors = new ArrayList<>();
        List<AbstractInterceptor> orderInterceptors = new ArrayList<>();

        for (AbstractInterceptor interceptor : interceptors) {
            Order order = AnnotationUtils.findAnnotation(interceptor.getClass(), Order.class);
            if (order == null) {
                unOrderinterceptors.add(interceptor);
            } else {
                orderInterceptors.add(interceptor);
            }
        }

        Collections.sort(orderInterceptors, (o1, o2) -> {
            Order order1 = AnnotationUtils.findAnnotation(o1.getClass(), Order.class);
            Order order2 = AnnotationUtils.findAnnotation(o2.getClass(), Order.class);
            return order1.value() - order2.value();
        });

        sortedInterceptors.addAll(orderInterceptors);
        sortedInterceptors.addAll(unOrderinterceptors);

        return sortedInterceptors;
    }
}
